﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReprojectWebMercator.Modules.glslNoise
{
    internal class glslNoise3d: GLSLFuncBase
    {
        vec3 mod289(vec3 x)
        {
            return x - floor(x * (1.0 / 289.0)) * 289.0;
        }

        vec4 mod289(vec4 x)
        {
            return x - floor(x * (1.0 / 289.0)) * 289.0;
        }

        vec4 permute(vec4 x)
        {
            return mod289(((x * 34.0) + 1.0) * x);
        }

        vec4 taylorInvSqrt(vec4 r)
        {
            return 1.79284291400159 - 0.85373472095314 * r;
        }

        public float snoise(vec3 v)
        {
            vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
            vec4 D = vec4(0.0, 0.5, 1.0, 2.0);

            // First corner
            vec3 i = floor(v + dot(v, C.get3("yyy")));
            vec3 x0 = v - i + dot(i, C.get3("xxx"));

            // Other corners
            vec3 g = step(x0.get3("yzx"), x0.get3("xyz"));
            vec3 l = 1.0 - g;
            vec3 i1 = min(g.get3("xyz"), l.get3("zxy"));
            vec3 i2 = max(g.get3("xyz"), l.get3("zxy"));

            //   x0 = x0 - 0.0 + 0.0 * C.xxx;
            //   x1 = x0 - i1  + 1.0 * C.xxx;
            //   x2 = x0 - i2  + 2.0 * C.xxx;
            //   x3 = x0 - 1.0 + 3.0 * C.xxx;
            vec3 x1 = x0 - i1 + C.get3("xxx");
            vec3 x2 = x0 - i2 + C.get3("yyy"); // 2.0*C.x = 1/3 = C.y
            vec3 x3 = x0 - D.get3("yyy");      // -1.0+3.0*C.x = -0.5 = -D.y
            
            // Permutations
            i = mod289(i);
            vec4 p = permute(permute(permute(
                       i.z + vec4(0.0, i1.z, i2.z, 1.0))
                     + i.y + vec4(0.0, i1.y, i2.y, 1.0))
                     + i.x + vec4(0.0, i1.x, i2.x, 1.0));
            // Gradients: 7x7 points over a square, mapped onto an octahedron.
            // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
            float n_ = 0.142857142857f; // 1.0/7.0
            vec3 ns = n_ * D.get3("wyz") - D.get3("xzx");
            
            vec4 j = p - 49.0 * floor(p * ns.z * ns.z);  //  mod(p,7*7)
            
            vec4 x_ = floor(j * ns.z);
            vec4 y_ = floor(j - 7.0 * x_);    // mod(j,N)

            vec4 x = x_ * ns.x + ns.get4("yyyy");
            vec4 y = y_ * ns.x + ns.get4("yyyy");
            vec4 h = 1.0 - abs(x) - abs(y);

            vec4 b0 = vec4(x.get2("xy"), y.get2("xy"));
            vec4 b1 = vec4(x.get2("zw"), y.get2("zw"));
            
            //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
            //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
            vec4 s0 = floor(b0) * 2.0 + 1.0;
            vec4 s1 = floor(b1) * 2.0 + 1.0;
            vec4 sh = -1*step(h, vec4(0.0));

            vec4 a0 = b0.get4("xzyw") + s0.get4("xzyw") * sh.get4("xxyy");
            vec4 a1 = b1.get4("xzyw") + s1.get4("xzyw") * sh.get4("zzww");
            
            vec3 p0 = vec3(a0.xy, h.x);
            vec3 p1 = vec3(a0.zw, h.y);
            vec3 p2 = vec3(a1.xy, h.z);
            vec3 p3 = vec3(a1.zw, h.w);

            //Normalise gradients
            vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
            
            p0 *= norm.x;
            p1 *= norm.y;
            p2 *= norm.z;
            p3 *= norm.w;

            // Mix final noise value
            vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
            m = m * m;
            return 42.0f * dot(m * m, vec4(dot(p0, x0), dot(p1, x1),
                                          dot(p2, x2), dot(p3, x3)));
        }
        public float fbm3d(vec3 x, int it)
        {
            float v = 0.0f;
            float a = 0.5f;
            vec3 shift = vec3(100);


            for (int i = 0; i < 32; ++i)
            {
                if (i < it)
                {
                    v += a * snoise(x);
                    x = x * 2.0 + shift;
                    a *= 0.5f;
                }
            }
            return v;
        }
    }
}
